/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.util.Arrays;

 class Spectralnorm{
    public final static int input = 500;
    public static double approximate(double[] u, double[] v, double[] buffer) {
        for (int count = 0; count < 10; count++) {
            multiplyAv(u, buffer);
            multiplyAtv(buffer, v);

            multiplyAv(v, buffer);
            multiplyAtv(buffer, u);
        }

        int vLen = v.length;
        double vBv = 0.0;
        double vv = 0.0;
        for (int i = 0; i < vLen; i++) {
            vBv += u[i] * v[i];
            vv += v[i] * v[i];
        }

        return Math.sqrt(vBv / vv);
    }

    public  static void multiplyAv(double[] v, double[]av) {
        int vLen = v.length;
        int avLen = av.length;
        for (int i = 0; i < avLen; i++) {
            av[i] = 0.0;
            for (int j = 0; j < vLen; j++) {
                double ij = i + j;
                double ret = 1.0 / (ij * (ij + 1) / 2 + i + 1);
                av[i] += ret * v[j];
            }
        }
    }

    public static void multiplyAtv(double[] v, double[] atv) {
        int vLen = v.length;
        int atvLen = atv.length;
        for (int i = 0; i < atvLen; i++) {
            atv[i] = 0;
            for (int j = 0; j < vLen; j++) {
                double ij = i + j;
                double ret = 1.0 / (ij * (ij + 1) / 2 + j + 1);
                atv[i] += ret * v[j];
            }
        }
    }

    public static void RunSpectralNorm() {
        int n = input;
        double[] u = new double[input];
        Arrays.fill(u, 1.0);
        double[] v = new double[input];
        Arrays.fill(v, 0.0);
        double[] buffer = new double[input];
        Arrays.fill(buffer, 0.0);

        double[] uF = u;
        double[] vF = v;
        double[] bufferF = buffer;

        double res = 0.0;
        double start = System.nanoTime();
        res = approximate(uF, vF, bufferF);
        double end = System.nanoTime();
        // ASSERT_FLOAT_EQ(res, 1.2742241159529055);
        double time = (end - start) / 1000000;
        System.out.println("res: " + res);
        System.out.println("Array Access - RunSpectralNorm:\t"+(time)+"\tms");
    }

}

class Benchmark {
    public static void main(String[] args) {
        Spectralnorm.RunSpectralNorm();
    }
}




